"""
copyright (c) 2009, paketka@gmail.com et. al
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

	* Redistributions of source code must retain the above copyright notice,
	  this list of conditions and the following disclaimer.

	* Redistributions in binary form must reproduce the above copyright notice,
	  this list of conditions and the following disclaimer in the documentation
	  and/or other materials provided with the distribution.

	* Neither the name of the <ORGANIZATION> nor the names of its contributors
	  may be used to endorse or promote products derived from this software
 	  without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import os
import os.path
import re
import Image
import datetime
from django.template import Library
from django import template
from settings import THUMBDIR, MEDIA_ROOT, IMG_THUMBS, MEDIA_URL

register = Library()

def removeThumb(imgFileName):
	"""
	deletes img thumb it must construct dirctory for given img thumb
	"""
	imgDir, fileName = os.path.split(imgFileName)
	thumbDir = os.path.join(imgDir, THUMBDIR)

	if os.access(thumbDir, os.F_OK):
		thumbs = os.listdir(thumbDir)
		baseFile, extension = os.path.splitext(fileName)
		pattern = None

		if extension and len(extension):
			pattern = re.compile(baseFile + '.*\\' + extension)
		else:
			pattern = re.compile(baseFile + '.*')

		for thumbFile in thumbs:
			if pattern.match(thumbFile) != None:
				os.remove(os.path.join(thumbDir, thumbFile))

	return

def getWH(whArgStr):
	"""
	function converts string to list of two ints
	"""

	retVal = [int(x) for x in whArgStr.split('x')]
	retVal = retVal[:2]
	l = len(retVal)

	if l == 0:
		retVal = [200, 200]
	elif l == 1:
		retVal[1] = retVal[0]

	return retVal

@register.filter
def thumb(imgPath, whArgStr = '200x200'):
	"""
	imgPath is string with path to original image file 
	xyArg is string in format 'WxH' (width x height), w, h are numbers

	the thumbnail will be stored into subdirectory called thumbs
	the thumbs resides in the same directory where orig img is present
	returns thumbnail of given image

	it creates subdirectory thumbs if it does not exist
	"""

	if imgPath == None or len(imgPath) == 0:
		return ''

	dirName, fileName = os.path.split(imgPath)
	dir, imgDir = os.path.split(dirName)
	thumbDir = os.path.join(dirName, THUMBDIR)

	basePathName, ext = fileName.rsplit('.', 1)
	thumbFileName = basePathName + '_' + whArgStr + '.' + ext
	thumbFullPath = os.path.join(thumbDir, thumbFileName)
	thumbFullPath = os.path.join(MEDIA_ROOT, thumbFullPath)
	#strip media path to get relative path to thumb
	thumbURL = MEDIA_URL + '/' + imgDir + '/' + THUMBDIR
	thumbURL += '/' + thumbFileName
	thumbDir = os.path.join(MEDIA_ROOT, thumbDir)

	if os.access(thumbDir, os.F_OK) == False:
		os.mkdir(thumbDir)


	# TODO: better check should be applied here
	if not os.path.exists(thumbFullPath):
		origImgFullPath = os.path.join(MEDIA_ROOT, imgPath)
		origImg = Image.open(origImgFullPath)
		origImg.thumbnail(getWH(whArgStr))
		origImg.save(thumbFullPath)

	return thumbURL

@register.inclusion_tag('table_header.html')
def table_header(model):
	"""
	Outputs table header from model class.
	it will do something like <span class="columnName"> xxxx </span>
	"""
	tableHeaders = None

	if hasattr(model, '_meta') and hasattr(model._meta, 'admin') and hasattr(model._meta.admin, 'fields'):
		for element in model._meta.admin.fields:
			if type(element) == type({}) and element.has_key('fields'):
				tableHeaders = element['fields']
				break
	else:
		tableHeaders = []
		for field in model._meta.fields:
			if field.editable:
				tableHeaders.append(field.name)

	return {'columns'	: tableHeaders, }


@register.inclusion_tag('table_row.html')
def table_row(instance):
	"""
	Outputs a single data row of model
	"""
	values = []
	columns = table_header(instance)
	for column in columns['columns']:
		values.append(getattr(instance, column))

	return { 'rowFields' : values }



class DatepickerNode(template.Node):
	datepickerTemplate = """
<!-- HEAD_include_js jquery-ui-1.7.custom.min.js -->


<script type="text/javascript">
	var enabledDays_{{ datepickerId }} = {};

	$(function() {
		var date = new Date();
		{% if year %}
			date.setMonth({{ month|add:"-1" }});
			date.setFullYear({{ year }});
		{% endif %}

		$("#{{ datepickerId|safe }}").datepicker({
			altFormat: 'yy/mm/dd/',
			dateFormat: 'yy/mm/dd/',
			defaultDate: date,
			changeMonth: true,
			changeYear: true,
			onChangeMonthYear: function(year, month) {
					finished_{{ datepickerId }} = false;
					jsonUrl = "{{ urlCalDataJSON }}/"
					jsonUrl += year + '/';
					jsonUrl += month + '/';
					$.ajax({
						url : jsonUrl,
						async : false,
						dataType : 'json',
						success : function(data) {
							enabledDays_{{ datepickerId }} = data;
						}
					});
				},
			beforeShowDay: function(date) {
					var retVal = [];

					if (enabledDays_{{ datepickerId|safe }}[date.getDate()] != 0) {
						retVal[0] = true;
						retVal[1] = '';
					}
					else {
						retVal[0] = false;
						retVal[1] = '';
					}
					
					return (retVal);
				},
			{% if onSelect %}
				onSelect: {{ onSelect }}
			{% else %}
				{% if jqGridId %}
					onSelect: function(dateText) {
						var oldUrl = $("#{{ jqGridId|safe }}").getGridParam("url");
						var baseUrl = oldUrl.replace(/\d{4}\/\d{1,2}\/\d{1,2}\//g,'');
						var newUrl = baseUrl + dateText;
						$("#{{ jqGridId|safe }}").setGridParam({ caption: dateText });
						$("#{{ jqGridId|safe }}").setGridParam({ url: newUrl }).trigger("reloadGrid");
					}
				{% else %}
					onSelect: function(dateText) {
							var urlToLoad = '{{ urlSelectedDateBase|safe }}'
							urlToLoad += '/' + dateText;
							window.location.assign(urlToLoad);
						}
				{% endif %}
			{% endif %}
			{% if dateStart and dateEnd %}
				,
				minDate: new Date({{ dateStart.year }}, {{ dateStart.month }} - 1, {{ dateStart.day }}),
				maxDate: new Date({{ dateEnd.year }}, {{ dateEnd.month }} - 1, {{ dateEnd.day }})
			{% endif %}
			}
		);
	});
</script>

<div id="{{ datepickerId }}">
</div>
	"""
	def __init__(self, divId, month, year, urlCalDays, urlDayData, jqGridId, dateStart, dateEnd, onSelect):
		"""
		constructor takes these parameters:
			month, year - the month we want to generate event calendar
			qs - query set with events,
			dateAttName - date attribute in QS objects,
			urlTempl - i.e. /showEventForDate/%d/%d, which will
				be turned to /showEventForDate/04/2008
			jqGridId - id of HTML element, where jqgrid object is bound,
				(or consumer for onSelect event)
		"""
		self.divId = divId
		self.month = month
		self.year = year
		self.urlCalDays = urlCalDays
		self.urlDayData = urlDayData
		self.dateStart = dateStart
		self.dateEnd = dateEnd
		self.jqGridId = jqGridId
		self.onSelect = onSelect

	def render(self, context):
		calDaysPattern = re.compile(r'(?P<baseUrl>.+)(?P<scratchIt>/\d{4}/\d{1,2}/?)', re.U)
		dayDataPattern = re.compile(r'(?P<baseUrl>.+)(?P<scratchIt>/\d{4}/\d{1,2}/\d{1,2}/?)', re.U)

		urlCalDays = self.urlCalDays.resolve(context)
		urlDayData = self.urlDayData.resolve(context)

		match = calDaysPattern.match(urlCalDays)
		if match != None:
			urlCalDays = match.group('baseUrl')

		match = dayDataPattern.match(urlDayData)
		if match != None:
			urlDayData = match.group('baseUrl')

		tmpl = template.Template(self.datepickerTemplate)

		if self.jqGridId != None:
			self.jqGridId = self.jqGridId.resolve(context)

		if self.onSelect != None:
			self.onSelect = self.onSelect.resolve(context)

		if self.dateStart != None:
			self.dateStart = self.dateStart.resolve(context)

		if self.dateEnd != None:
			self.dateEnd = self.dateEnd.resolve(context)

		cx = template.Context({
				'datepickerId' : self.divId.resolve(context),
				'urlCalDataJSON' : urlCalDays,
				'urlSelectedDateBase' : urlDayData,
				'month' : self.month.resolve(context),
				'year' : self.year.resolve(context),
				'dateStart' : self.dateStart,
				'dateEnd' : self.dateEnd,
				'jqGridId' : self.jqGridId,
				'onSelect' : self.onSelect,
			})
		
		return tmpl.render(cx)


@register.tag
def datepicker(parser, token):
	"""
	the urlCalDays will refere to some particular cal url i.e.
		json/jednaciDny/2008/03
	we need to turn this instance refering to given month, to generic part like
	json/jednaciDny/

	same applies to urlDayData argument.

	The template tag options are screwed up a bit right now.  In case three
	options are passed:
		{% datepicker "html id" calData urlDay %}
	the simple datepicker will be generated:
		"html id" 	- is element ID, where datepicker will be bound

		calData		- is URL, where from 'busy days' will be fetched i.e.
					  json/jednaciDny/2008/03
		urlDay		- URL to details about particular busy day, once user will
					  click to particular busy day, default onSelect() method
					  will be invoked. Method will open a new tab with busy day
					  details
	the datepicker will be preset to current month.

	if you don't want to datepicker to be preset to current month, you can
	pass a month/year as a datepicker initial value, then you need to
	invoke datepicker as follows:
		{% datepicker "html id" calData urlDay year month %}

	sometimes you don't want to use default action (to open tab with desired
	detail page). Another 'default' action is to pass a new AJAX url to jqGrid,
	to allow jqGrid to fetch data for another month, without opening a new tab.
	you need to pass an extra argument to datepicker then: element id of jqgrid
	to update its url. It will look as follows then:
		{% datepicker "html id" calData urlDay year month "jqGridId" %}
	
	If you don't want to use any of predefined onSelect callback, you might
	want to supply your own. to achieve this a keyword argument must be used,
	where jqGridId is passed:
		{% datepicker "html id" calData urlDay year month onSelect="myOnSelect" %}

	Remember it's the arguments must be passed in order, which was introduced here.
	The only keyword argument is onSelect. onSelect must be passed as last.

	Time to time we need to define a range - startDate, endDate - for a grid.
	In case we don't want to define callback, then following templatetag should
	be used:
		{% datepicker "html id" calData urlDay year month start end %}

	if callback should be used, pass it again as a last argument:
		{% datepicker "html id" calData urlDay year month start end onSelect="myOnSelect" %}
	"""

	params = token.split_contents()
	
	if len(params) == 4:
		divId = params[1]
		urlCalDays = params[2]
		urlDayData = params[3]
		today = datetime.datetime.today()
		year = str(today.year)
		month = str(today.month)
		dateStart = None
		dateEnd = None
		jqGridId = None
		onSelect = None
	elif len(params) == 6:
		divId = params[1]
		urlCalDays = params[2]
		urlDayData = params[3]
		year = params[4]
		month = params[5]
		dateStart = None
		dateEnd = None
		jqGridId = None
		onSelect = None
	elif len(params) == 7:
		divId = params[1]
		urlCalDays = params[2]
		urlDayData = params[3]
		year = params[4]
		month = params[5]
		dateStart = None
		dateEnd = None

		onSelect = None
		jqGridId = None
		if '=' in params[6]:
			k, v = params[6].split('=', 1)
			if k != 'onSelect':
				errorStr = "Unexpected kw arg (%s)" % k
				raise template.TemplateSyntaxError, errorStr

			onSelect = parser.compile_filter(v)
		else:
			jqGridId = parser.compile_filter(params[6])
	elif len(params) == 8:
		divId = params[1]
		urlCalDays = params[2]
		urlDayData = params[3]
		year = params[4]
		month = params[5]
		dateStart = parser.compile_filter(params[6])
		dateEnd = parser.compile_filter(params[7])
		onSelect = None
		jqGridId = None
	elif len(params) == 9:
		divId = params[1]
		urlCalDays = params[2]
		urlDayData = params[3]
		year = params[4]
		month = params[5]
		dateStart = parser.compile_filter(params[6])
		dateEnd = parser.compile_filter(params[7])

		onSelect = None
		jqGridId = None
		if '=' in params[8]:
			k, v = params[8].split('=', 1)
			if k != 'onSelect':
				errorStr = "Unexpected kw arg (%s)" % k
				raise template.TemplateSyntaxError, errorStr

			onSelect = parser.compile_filter(v)
		else:
			jqGridId = parser.compile_filter(params[6])
		
	else:
		errorStr =  "%s requires 3, 5, 6, 7 or 8 arguments" % token.split_contents()[0] 
		raise template.TemplateSyntaxError, errorStr
	
	return DatepickerNode(
					divId = parser.compile_filter(divId),
					year = parser.compile_filter(year),
					month = parser.compile_filter(month),
					urlCalDays = parser.compile_filter(urlCalDays),
					urlDayData = parser.compile_filter(urlDayData),
					dateStart = dateStart,
					dateEnd = dateEnd,
					jqGridId = jqGridId,
					onSelect = onSelect
				)

